/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrGpuResourceCacheAccess_DEFINED
#define GrGpuResourceCacheAccess_DEFINED

#include "src/gpu/ganesh/GrGpuResource.h"
#include "src/gpu/ganesh/GrGpuResourcePriv.h"

namespace skiatest {
class Reporter;
} // namespace skiatest

/* *
 * This class allows GrResourceCache increased privileged access to GrGpuResource objects.
 */
class GrGpuResource::CacheAccess {
private:
    /* * The cache is allowed to go from no refs to 1 ref. */
    void ref()
    {
        fResource->addInitialRef();
    }

    /* *
     * Is the resource currently cached as scratch? This means it is cached, has a valid scratch
     * key, and does not have a unique key.
     */
    bool isScratch() const
    {
        return !fResource->getUniqueKey().isValid() && fResource->fScratchKey.isValid() &&
            GrBudgetedType::kBudgeted == fResource->resourcePriv().budgetedType();
    }

    bool isUsableAsScratch() const
    {
        return this->isScratch() && !fResource->internalHasRef();
    }

    /* *
     * Called by the cache to delete the resource under normal circumstances.
     */
    void release()
    {
        fResource->release();
        if (!fResource->hasRef() && fResource->hasNoCommandBufferUsages()) {
            delete fResource;
        }
    }

    /* *
     * Called by the cache to delete the resource when the backend 3D context is no longer valid.
     */
    void abandon()
    {
        fResource->abandon();
        if (!fResource->hasRef() && fResource->hasNoCommandBufferUsages()) {
            delete fResource;
        }
    }

    /* * Called by the cache to assign a new unique key. */
    void setUniqueKey(const skgpu::UniqueKey &key)
    {
        fResource->fUniqueKey = key;
    }

    /* * Is the resource ref'ed */
    bool hasRef() const
    {
        return fResource->hasRef();
    }
    bool hasRefOrCommandBufferUsage() const
    {
        return this->hasRef() || !fResource->hasNoCommandBufferUsages();
    }

    /* * Called by the cache to make the unique key invalid. */
    void removeUniqueKey()
    {
        fResource->fUniqueKey.reset();
    }

    uint32_t timestamp() const
    {
        return fResource->fTimestamp;
    }
    void setTimestamp(uint32_t ts)
    {
        fResource->fTimestamp = ts;
    }

    void setTimeWhenResourceBecomePurgeable()
    {
        SkASSERT(fResource->isPurgeable());
        fResource->fTimeWhenBecamePurgeable = skgpu::StdSteadyClock::now();
    }
    /* *
     * Called by the cache to determine whether this resource should be purged based on the length
     * of time it has been available for purging.
     */
    skgpu::StdSteadyClock::time_point timeWhenResourceBecamePurgeable()
    {
        SkASSERT(fResource->isPurgeable());
        return fResource->fTimeWhenBecamePurgeable;
    }

    int *accessCacheIndex() const
    {
        return &fResource->fCacheArrayIndex;
    }

    CacheAccess(GrGpuResource *resource) : fResource(resource) {}
    CacheAccess(const CacheAccess &that) : fResource(that.fResource) {}
    CacheAccess &operator = (const CacheAccess &) = delete;

    // No taking addresses of this type.
    const CacheAccess *operator&() const = delete;
    CacheAccess *operator&() = delete;

    GrGpuResource *fResource;

    friend class GrGpuResource;                                           // to construct/copy this type.
    friend class GrResourceCache;                                         // to use this type
    friend void test_unbudgeted_to_scratch(skiatest::Reporter *reporter); // for unit testing
};

inline GrGpuResource::CacheAccess GrGpuResource::cacheAccess()
{
    return CacheAccess(this);
}

inline const GrGpuResource::CacheAccess GrGpuResource::cacheAccess() const
{ // NOLINT(readability-const-return-type)
    return CacheAccess(const_cast<GrGpuResource *>(this));
}

#endif
